package wrapper

import (
	"log"
	"regexp"
	"strings"

	"github.com/Kindling-project/chaos-backend/api"
	"github.com/chaos-mesh/chaos-mesh/api/v1alpha1"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)

type ToChaosVO func(obj interface{}) []api.Chaos

func NetworkChaosToChaosVO(obj *interface{}) []api.Chaos {
	if obj == nil {
		return nil
	}

	var result []api.Chaos = make([]api.Chaos, 0)

	switch obj := (*obj).(type) {
	case v1alpha1.NetworkChaosList:
		for _, chaos := range obj.Items {
			if v, ok := chaos.Labels["kindling-chaos"]; ok && v == "train-ticket" {
				result = append(result, v1networkchaosToChaosVO(&chaos))
			}
		}
	case v1alpha1.NetworkChaos:
		if v, ok := obj.Labels["kindling-chaos"]; ok && v == "train-ticket" {
			result = append(result, v1networkchaosToChaosVO(&obj))
		}
	default:
		log.Printf("Error: %+v", obj)
	}

	return result
}

func v1networkchaosToChaosVO(chaos *v1alpha1.NetworkChaos) api.Chaos {
	var status = api.Injecting
	for _, condition := range chaos.Status.Conditions {
		if condition.Type == v1alpha1.ConditionAllInjected &&
			condition.Status == corev1.ConditionTrue {
			status = api.Injected
			break
		}
	}

	if chaos.Status.Experiment.DesiredPhase == v1alpha1.StoppedPhase {
		status = api.InjectFailed
	}

	var service string
	instances := getKeys(chaos.Status.Instances)
	if status == api.Injected {
		for i := 0; i < len(instances); i++ {
			service = getService(instances[i])
			if service != "" {
				break
			}
		}
	}

	if service == "" {
		service = chaos.Annotations["target"]
	}

	var desc, complexAction string
	if chaos.Annotations != nil {
		if describe, find := chaos.Annotations["describe"]; find {
			desc = describe
		}
		if action, find := chaos.Annotations["complex-action"]; find {
			complexAction = action
		}
	}

	return api.Chaos{
		ChaosType:     "networkchaos",
		ComplexAction: complexAction,
		Instance:      instances,
		Desc:          desc,
		Params: []api.ChaosParams{
			{Name: "instance", Value: service},
		},
		Status: status,
	}
}

func HTTPChaosToChaosVO(obj *interface{}) []api.Chaos {
	if obj == nil {
		return nil
	}

	var result []api.Chaos = make([]api.Chaos, 0)

	switch obj := (*obj).(type) {
	case v1alpha1.HTTPChaosList:
		for _, chaos := range obj.Items {
			if v, ok := chaos.Labels["kindling-chaos"]; ok && v == "train-ticket" {
				result = append(result, v1httpchaosToChaosVO(&chaos))
			}
		}
	case v1alpha1.HTTPChaos:
		if v, ok := obj.Labels["kindling-chaos"]; ok && v == "train-ticket" {
			result = append(result, v1httpchaosToChaosVO(&obj))
		}
	default:
		log.Printf("Error: %+v", obj)
	}

	return result
}

func v1httpchaosToChaosVO(chaos *v1alpha1.HTTPChaos) api.Chaos {
	var status = api.Injecting
	for _, condition := range chaos.Status.Conditions {
		if condition.Type == v1alpha1.ConditionAllInjected &&
			condition.Status == corev1.ConditionTrue {
			status = api.Injected
			break
		}
	}

	if chaos.Status.Experiment.DesiredPhase == v1alpha1.StoppedPhase {
		status = api.InjectFailed
	}

	var service string
	instances := getKeys(chaos.Status.Instances)
	if status == api.Injected {
		for i := 0; i < len(instances); i++ {
			service = getService(instances[i])
			if service != "" {
				break
			}
		}
	}

	if service == "" {
		service = chaos.Annotations["target"]
	}

	var desc, complexAction string
	if chaos.Annotations != nil {
		if describe, find := chaos.Annotations["describe"]; find {
			desc = describe
		}
		if action, find := chaos.Annotations["complex-action"]; find {
			complexAction = action
		}
	}

	return api.Chaos{
		ChaosType:     "httpchaos",
		ComplexAction: complexAction,
		Instance:      instances,
		Desc:          desc,
		Params: []api.ChaosParams{
			{Name: "instance", Value: service},
		},
		Status: status,
	}
}

func IOChaosToChaosVO(obj *interface{}) []api.Chaos {
	if obj == nil {
		return nil
	}

	var result []api.Chaos = make([]api.Chaos, 0)

	switch obj := (*obj).(type) {
	case v1alpha1.IOChaosList:
		for _, chaos := range obj.Items {
			if v, ok := chaos.Labels["kindling-chaos"]; ok && v == "train-ticket" {
				result = append(result, v1iochaosToChaosVO(&chaos))
			}
		}
	case v1alpha1.IOChaos:
		if v, ok := obj.Labels["kindling-chaos"]; ok && v == "train-ticket" {
			result = append(result, v1iochaosToChaosVO(&obj))
		}
	default:
		log.Printf("Error: %+v", obj)
	}

	return result
}

func v1iochaosToChaosVO(chaos *v1alpha1.IOChaos) api.Chaos {
	var status = api.Injecting
	for _, condition := range chaos.Status.Conditions {
		if condition.Type == v1alpha1.ConditionAllInjected &&
			condition.Status == corev1.ConditionTrue {
			status = api.Injected
			break
		}
	}

	if chaos.Status.Experiment.DesiredPhase == v1alpha1.StoppedPhase {
		status = api.InjectFailed
	}

	var service string
	instances := getKeys(chaos.Status.Instances)
	if status == api.Injected {
		for i := 0; i < len(instances); i++ {
			service = getService(instances[i])
			if service != "" {
				break
			}
		}
	}

	if service == "" {
		service = chaos.Annotations["target"]
	}

	var desc, complexAction string
	if chaos.Annotations != nil {
		if describe, find := chaos.Annotations["describe"]; find {
			desc = describe
		}
		if action, find := chaos.Annotations["complex-action"]; find {
			complexAction = action
		}
	}

	return api.Chaos{
		ChaosType:     "iochaos",
		ComplexAction: complexAction,
		Instance:      instances,
		Desc:          desc,
		Params: []api.ChaosParams{
			{Name: "instance", Value: service},
		},
		Status: status,
	}
}

// jvm中用到的资源转换
func JVMChaosToChaosVO(obj *interface{}) []api.Chaos {
	if obj == nil {
		return nil
	}

	var result []api.Chaos = make([]api.Chaos, 0)

	switch obj := (*obj).(type) {
	case v1alpha1.JVMChaosList:
		for _, chaos := range obj.Items {
			if v, ok := chaos.Labels["kindling-chaos"]; ok && v == "train-ticket" {
				result = append(result, v1jvmchaosToChaosVO(&chaos))
			}
		}
	case v1alpha1.JVMChaos:
		if v, ok := obj.Labels["kindling-chaos"]; ok && v == "train-ticket" {
			result = append(result, v1jvmchaosToChaosVO(&obj))
		}
	default:
		log.Printf("Error: %+v", obj)
	}

	return result
}

// 转换JVMChaos的资源
func v1jvmchaosToChaosVO(chaos *v1alpha1.JVMChaos) api.Chaos {
	var status = api.Injecting
	for _, condition := range chaos.Status.Conditions {
		if condition.Type == v1alpha1.ConditionAllInjected &&
			condition.Status == corev1.ConditionTrue {
			status = api.Injected
			break
		}
	}

	if chaos.Status.Experiment.DesiredPhase == v1alpha1.StoppedPhase {
		status = api.InjectFailed
	}

	var ids = make([]string, 0)
	for _, record := range chaos.Status.Experiment.Records {
		ids = append(ids, record.Id)
	}

	var service string
	if status == api.Injected {
		if len(ids) > 0 {
			service = getService(ids[0])
		}
	}

	if service == "" {
		service = chaos.Annotations["target"]
	}

	var desc, complexAction string
	if chaos.Annotations != nil {
		if describe, find := chaos.Annotations["describe"]; find {
			desc = describe
		}
		if action, find := chaos.Annotations["complex-action"]; find {
			complexAction = action
		}
	}

	return api.Chaos{
		ChaosType:     "jvmchaos",
		ComplexAction: complexAction,
		Instance:      ids,
		Params: []api.ChaosParams{
			{Name: "instance", Value: service},
		},
		Desc:   desc,
		Status: status,
	}
}

func StressChaosToChaosVO(obj *interface{}) []api.Chaos {
	if obj == nil {
		return nil
	}

	var result []api.Chaos = make([]api.Chaos, 0)

	switch obj := (*obj).(type) {
	case v1alpha1.StressChaosList:
		for _, chaos := range obj.Items {
			if v, ok := chaos.Labels["kindling-chaos"]; ok && v == "train-ticket" {
				result = append(result, v1stresschaosToChaosVO(&chaos))
			}
		}
	case v1alpha1.StressChaos:
		if v, ok := obj.Labels["kindling-chaos"]; ok && v == "train-ticket" {
			result = append(result, v1stresschaosToChaosVO(&obj))
		}
	default:
		log.Printf("Error: %+v", obj)
	}

	return result

}

func v1stresschaosToChaosVO(chaos *v1alpha1.StressChaos) api.Chaos {
	var status = api.Injecting
	for _, condition := range chaos.Status.Conditions {
		if condition.Type == v1alpha1.ConditionAllInjected &&
			condition.Status == corev1.ConditionTrue {
			status = api.Injected
			break
		}
	}

	if chaos.Status.Experiment.DesiredPhase == v1alpha1.StoppedPhase {
		status = api.InjectFailed
	}

	var service string
	instances := getStressChaosKeys(chaos.Status.Instances)
	if status == api.Injected {
		for i := 0; i < len(instances); i++ {
			service = getService(instances[i])
			if service != "" {
				break
			}
		}
	}

	if service == "" {
		service = chaos.Annotations["target"]
	}

	var desc, complexAction string
	if chaos.Annotations != nil {
		if describe, find := chaos.Annotations["describe"]; find {
			desc = describe
		}
		if action, find := chaos.Annotations["complex-action"]; find {
			complexAction = action
		}
	}
	return api.Chaos{
		ChaosType:     "stresschaos",
		ComplexAction: complexAction,
		Instance:      instances,
		Desc:          desc,
		Params: []api.ChaosParams{
			{Name: "instance", Value: service},
		},
		Status: status,
	}
}

func UnstructuredChaosToVO(chaos *unstructured.Unstructured) api.Chaos {
	// 从元数据中提取complexAction信息
	var desc, complexAction string
	anno := chaos.GetAnnotations()
	if anno != nil {
		if describe, find := anno["describe"]; find {
			desc = string(describe)
		}
		if action, find := anno["complex-action"]; find {
			complexAction = string(action)
		}
	}
	chaosType := strings.ToLower(chaos.GetKind())
	target := anno["target"]

	// 分析注入状态
	var status = api.Injecting
	conditions, find, err := unstructured.NestedSlice(chaos.Object, "status", "conditions")
	if err != nil {
		// conditions is not a slice , unexpected branch
	}
	if find {
		for _, conditionItem := range conditions {
			condition := conditionItem.(map[string]any)
			conditionType, find, err := unstructured.NestedString(condition, "type")
			if !find || err != nil {
				continue
			}
			conditionsStatus, find, err := unstructured.NestedString(condition, "status")
			if !find || err != nil {
				continue
			}
			if conditionType == string(v1alpha1.ConditionAllInjected) && conditionsStatus == string(corev1.ConditionTrue) {
				status = api.Injected
				break
			}
		}
		desiredPhase, find, err := unstructured.NestedString(chaos.Object, "status", "experiment", "desiredPhase")
		if err == nil && find && desiredPhase == string(v1alpha1.StoppedPhase) {
			status = api.InjectFailed
		}
	}
	// 提取受影响的实例信息
	var instancesIds []string = make([]string, 0)
	instance, find, err := unstructured.NestedMap(chaos.Object, "status", "instances")
	if err == nil && find {
		for key := range instance {
			instancesIds = append(instancesIds, key)
		}
	}

	return api.Chaos{
		ChaosType:     chaosType,
		ComplexAction: complexAction,
		Instance:      instancesIds,
		Desc:          desc,
		Params: []api.ChaosParams{
			{Name: "instance", Value: target},
		},
		Status: status,
	}
}

func getStressChaosKeys(m map[string]v1alpha1.StressInstance) []string {
	keys := make([]string, 0, len(m))
	for k := range m {
		keys = append(keys, k)
	}
	return keys
}

func getKeys(m map[string]int64) []string {
	keys := make([]string, 0, len(m))
	for k := range m {
		keys = append(keys, k)
	}
	return keys
}

var serviceName = regexp.MustCompile("train-ticket/(.*?-service).*")

func getService(instance string) string {
	if matches := serviceName.FindStringSubmatch(instance); len(matches) > 1 {
		return matches[1]
	} else {
		return ""
	}
}
